package de.fzi.kasma.learner.function.kernel.rdf;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.PreloadConfig;

import edu.unika.aifb.graphindex.util.StringSplitter;
import edu.unika.aifb.graphindex.util.Util;

public class KernelCache {
	private Database m_nodeDb;
	private Database m_colorDb;
	private Database m_partitionColorDb;
	private Database m_colorPartitionDb;

	private final static Logger log = Logger.getLogger(KernelCache.class);

	private Map<Integer, Color> m_colorCache;
	private Map<Integer, Partition> m_partitionCache;

	public KernelCache(File cacheDir) throws DatabaseException, IOException {
		EnvironmentConfig config = new EnvironmentConfig();
		config.setTransactional(false);
		config.setAllowCreate(true);

		Environment env = new Environment(cacheDir, config);
		initialize(env);
	}

	private void initialize(Environment env) throws DatabaseException {
		DatabaseConfig config = new DatabaseConfig();
		config.setTransactional(false);
		config.setAllowCreate(true);
		config.setSortedDuplicates(true);
		config.setDeferredWrite(true);

		m_nodeDb = env.openDatabase(null, "nbc", config);

		config = new DatabaseConfig();
		config.setTransactional(false);
		config.setAllowCreate(true);
		config.setSortedDuplicates(true);
		config.setDeferredWrite(true);

		m_colorDb = env.openDatabase(null, "cbc", config);

		config = new DatabaseConfig();
		config.setTransactional(false);
		config.setAllowCreate(true);
		config.setSortedDuplicates(true);
		config.setDeferredWrite(true);

		m_partitionColorDb = env.openDatabase(null, "pcb", config);

		config = new DatabaseConfig();
		config.setTransactional(false);
		config.setAllowCreate(true);
		config.setSortedDuplicates(true);
		config.setDeferredWrite(true);

		m_colorPartitionDb = env.openDatabase(null, "cpb", config);

		m_colorCache = new HashMap<Integer, Color>();
		m_partitionCache = new HashMap<Integer, Partition>();

		PreloadConfig pc = new PreloadConfig();
		pc.setMaxMillisecs(2000);
		m_nodeDb.preload(pc);
	}

	public void close() throws DatabaseException {
		m_nodeDb.close();
		m_colorDb.close();
	}

	public Color createColor() {
		Color b = new Color(this);
		m_colorCache.put(b.getId(), b);
		return b;
	}

	public Partition createPartition() throws Exception {
		Partition p = new Partition(this);
		m_partitionCache.put(p.getId(), p);
		return p;
	}

	public void setNodeColor(Integer node, Color color) {
		try {
			m_nodeDb.putNoDupData(null,
					new DatabaseEntry(Util.intToBytes(node)),
					new DatabaseEntry(Util.intToBytes(color.getId())));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void addNodesToColor(Color color, Set<Integer> elements) {
		try {
			for (Integer key : elements) {

				m_colorDb.putNoDupData(null,
						new DatabaseEntry(Util.intToBytes(color.getId())),
						new DatabaseEntry(Util.intToBytes(key)));
			}
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	public void addColorToPartition(Partition p, Color color) {
		try {
			m_partitionColorDb.putNoDupData(null,
					new DatabaseEntry(Util.intToBytes(p.getId())),
					new DatabaseEntry(Util.intToBytes(color.getId())));
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	public void addPartitionToColor(Partition p, Color color) {
		try {
			m_colorPartitionDb.putNoDupData(null,
					new DatabaseEntry(Util.intToBytes(color.getId())),
					new DatabaseEntry(Util.intToBytes(p.getId())));
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	public Set<Color> getPartitionColors(Integer element) {
		CursorConfig config = new CursorConfig();
		config.setReadUncommitted(true);
		Cursor cursor = m_partitionColorDb.openCursor(null, null);
		Set<Color> colors = new HashSet<Color>();

		DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(element));
		DatabaseEntry out = new DatabaseEntry();
		try {
			while (cursor.getNext(foundKey, out, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
				Color dataString = m_colorCache.get(Util.bytesToInt(out
						.getData()));
				colors.add(dataString);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return colors;
	}

	public Set<Partition> getColorPartitions(Integer element) {
		CursorConfig config = new CursorConfig();
		config.setReadUncommitted(true);
		Cursor cursor = m_colorPartitionDb.openCursor(null, null);
		Set<Partition> partitions = new HashSet<Partition>();

		DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(element));
		DatabaseEntry out = new DatabaseEntry();
		try {
			while (cursor.getNext(foundKey, out, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
				Partition dataString = m_partitionCache.get(Util.bytesToInt(out
						.getData()));
				partitions.add(dataString);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return partitions;
	}

	public Color getColor(Integer id) {
		return m_colorCache.get(id);
	}

	public Set<Color> getColors(Integer element) {
		CursorConfig config = new CursorConfig();
		config.setReadUncommitted(true);
		Cursor cursor = m_nodeDb.openCursor(null, null);
		Set<Color> colors = new HashSet<Color>();

		DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(element));
		DatabaseEntry out = new DatabaseEntry();
		try {
			while (cursor.getNext(foundKey, out, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
				Color dataString = m_colorCache.get(Util.bytesToInt(out
						.getData()));
				colors.add(dataString);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return colors;
	}

	public Set<Integer> getElements(Partition p, Color color) {
		try {
			Set<Integer> elements = new HashSet<Integer>();
			if (m_partitionColorDb.get(null,
					new DatabaseEntry(Util.intToBytes(p.getId())),
					new DatabaseEntry(Util.intToBytes(color.getId())), null) != null) {
				CursorConfig config = new CursorConfig();
				config.setReadUncommitted(true);
				Cursor cursor = m_colorDb.openCursor(null, null);
				DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(color.getId()));
				DatabaseEntry out = new DatabaseEntry();
				while(cursor.getNext(foundKey, out, LockMode.DEFAULT)==OperationStatus.SUCCESS){
					Integer element = Util.bytesToInt(out.getData());
					elements.add(element);
				}
			}else{
				return null;
			}
			return elements;
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void removeElement(Color color, Integer element) {
		try {
			CursorConfig config = new CursorConfig();
			config.setReadUncommitted(true);
			Cursor cursor = m_colorDb.openCursor(null, null);
			DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(color
					.getId()));
			DatabaseEntry out = new DatabaseEntry(Util.intToBytes(element));
			if (cursor.getSearchBoth(foundKey, out, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
				cursor.delete();
			}
		} catch (DatabaseException e) {
		}
	}

	public void removeColor(Partition p, Color color) {
		try {

			Set<Integer> colorNodes = getElements(p, color);
			for (Integer node : colorNodes) {
				clearColor(node, color);
			}
			m_colorCache.remove(color.getId());
			m_colorDb.delete(null,
					new DatabaseEntry(Util.intToBytes(color.getId())));
		} catch (DatabaseException e) {
			e.printStackTrace();
		}
	}

	public void clearColor(Integer element, Color color) {
		CursorConfig config = new CursorConfig();
		config.setReadUncommitted(true);
		Cursor cursor = m_nodeDb.openCursor(null, null);
		DatabaseEntry foundKey = new DatabaseEntry(Util.intToBytes(element));
		DatabaseEntry out = new DatabaseEntry(Util.intToBytes(color.getId()));
		if (cursor.getSearchBoth(foundKey, out, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
			cursor.delete();
		}
	}
}
